package repository

import (
	"fmt"
	"server/application/db/dbinit"
	"server/pkg/utils"
	"server/pkg/where"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

// Create 创建
func Create[TEntity Entities, TDtoCreate DtoCreates, TDtoInfo DtoInfos](dtoCreate *TDtoCreate) (*TDtoInfo, error) {
	entityInfo := new(TEntity)               //为分配一个内存空间
	utils.DtoToEntity(dtoCreate, entityInfo) //将dto转成entity
	result := dbinit.GetDbInstance().Create(entityInfo)
	var dtoInfo = new(TDtoInfo)
	utils.DtoToEntity(entityInfo, dtoInfo)
	return dtoInfo, result.Error
}

// CreateBatch 批量创建,WhenErrorUpdate表示遇到冲突时是否更新记录
func CreateBatch[TEntity Entities, TDtoCreate DtoCreates](dtoCreateData []TDtoCreate, WhenErrorUpdate bool) (int64, error) {
	tx := dbinit.GetDbInstance().Begin()   //开启事务
	entityCreateList := make([]TEntity, 0) //保存创建的实体列表
	for i := 0; i < len(dtoCreateData); i++ {
		entityInfo := new(TEntity)
		utils.DtoToEntity(&dtoCreateData[i], entityInfo) //将dto转成entity
		entityCreateList = append(entityCreateList, *entityInfo)
	}
	var result *gorm.DB
	if WhenErrorUpdate {
		// 在冲突时(如唯一字段)，更新除主键以外的所有列到新值。
		result = tx.
			Clauses(clause.OnConflict{UpdateAll: true}).
			Create(entityCreateList)
	} else {
		//遇到冲突时跳过
		result = tx.
			Clauses(clause.OnConflict{DoNothing: true}).
			Create(entityCreateList)
	}
	if result.Error != nil {
		tx.Rollback() //回滚
	} else {
		tx.Commit() //提交事务
	}

	return result.RowsAffected, result.Error //返回插入的条数，和错误
}

// FindOne 查找一个
func FindOne[TEntity Entities, TDtoInfo DtoInfos](wheres []*where.Condition, getSoftDelData bool) (*TDtoInfo, error) {
	var entityResult *TEntity
	whereStr, whereValues, err := where.ConvertToGormWhere(wheres)
	if err != nil {
		return nil, err
	}
	tx := dbinit.GetDbInstance().Where(whereStr, whereValues...)
	if getSoftDelData {
		//Unscoped()，如果关联模型被软删除了，它们将被包括在查询结果中
		tx = tx.Unscoped()
	}
	if err = tx.Find(&entityResult).Error; err != nil {
		return nil, err
	}
	var dtoInfo = new(TDtoInfo)
	utils.DtoToEntity(entityResult, dtoInfo)
	return dtoInfo, err
}

// FindList 查找列表  --getSoftDelData是否获取软删除数据
func FindList[TEntity Entities, TDtoInfo DtoInfos](
	wheres []*where.Condition, extra *where.Extra, getSoftDelData bool) (
	[]*TDtoInfo, error) {
	var resultList []*TEntity

	var dtoInfoList []*TDtoInfo
	tx := dbinit.GetDbInstance()
	if extra == nil { //传入空值nil则使用零值
		extra = &where.Extra{}
	}
	//排序方式
	if extra.OrderByColumn != "" {
		if extra.OrderByDesc {
			tx = tx.Order(extra.OrderByColumn + " desc")
		} else {
			tx = tx.Order(extra.OrderByColumn + " desc")
		}
	}
	//limit
	if extra.PageSize > 0 {
		tx = tx.Limit(extra.PageSize)
	}

	//offset条件
	if extra.PageNum > 0 {
		tx = tx.Offset(calculateOffset(extra.PageNum, extra.PageSize))
	} else {
		//取消offset条件
		tx = tx.Offset(-1)
	}

	//where 构造
	whereStr, whereValues, err := where.ConvertToGormWhere(wheres)
	if err != nil {
		return dtoInfoList, err
	}
	tx = tx.Where(whereStr, whereValues...)
	if getSoftDelData {
		//Unscoped()，如果关联模型被软删除了，它们将被包括在查询结果中
		tx = tx.Unscoped()
	}

	err = tx.Find(&resultList).Error
	if err != nil {
		return dtoInfoList, err
	}
	sqlStatement := tx.Statement.SQL.String()
	fmt.Print(sqlStatement)
	for _, entityInfo := range resultList {
		var dtoInfo = new(TDtoInfo)
		utils.DtoToEntity(entityInfo, dtoInfo)
		dtoInfoList = append(dtoInfoList, dtoInfo)
	}

	return dtoInfoList, nil
}

// 根据页码和页面大小计算偏移量
func calculateOffset(page, pageSize int) int {
	offset := (page - 1) * pageSize
	return offset
}

// Update  指定配置更新
// 要特别注意零值问题，updateColumnsCfg可以指定要更新的字段类型或者字段名，如果不传入则默认更新非零值字段。
// updateColumnsCfg只传入一个值时会先跟update_config里面的几个常量进行匹配,
// 匹配到了则按照里面的规则进行更新，如UpdateColumnsNotZeroAndNumber表示更新所有非0值和整型的字段
// 否则会按照其是一个指定字段来更新，传入多个值是会认为是手动指定要更新字段。
func Update[TEntity Entities, TDtoUpdate DtoUpdates](
	wheres []*where.Condition,
	dtoUpdate *TDtoUpdate,
	updateColumnsCfg ...string, //强制更新的字段，不管是否为零值
) error {
	updateEntity := new(TEntity) //为分配一个内存空间

	utils.DtoToEntity(dtoUpdate, updateEntity) //将dto转成entity

	updateColumns := make([]string, 0) //要更新的字段
	if updateColumnsCfg != nil && len(updateColumnsCfg) > 0 {
		if len(updateColumnsCfg) == 1 && utils.ArrContains(UpdateTypList, updateColumnsCfg[0]) {
			//按照指定的更新规则来更新
			updateColumns = GetUpdateColumns(dtoUpdate, updateColumnsCfg[0])
		} else {
			//多个值 或1个值但是不属于类型指定 则组装更新的字段列表
			for _, column := range updateColumnsCfg {
				updateColumns = append(updateColumns, column)
			}
		}
	}

	whereStr, whereValues, err := where.ConvertToGormWhere(wheres)
	if err != nil {
		return err
	}

	dbx := dbinit.GetDbInstance().Model(new(TEntity)).Where(whereStr, whereValues...)
	if updateColumns == nil || len(updateColumns) == 0 {
		//不指定字段，gorm内部会默认更新所有非零值数据
		dbx.Updates(updateEntity)
	} else {
		//指定要更新的字段（解决零值问题）
		dbx.Select(updateColumns).Updates(updateEntity)
	}

	if dbx.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}

	return dbx.Error
}

// Delete 删除,softDelete--是否软删除。deleted_at字段存在时默认会软删除
func Delete[TEntity Entities](wheres []*where.Condition, softDelete bool) error {
	tx := dbinit.GetDbInstance().Begin() //开启实物
	whereStr, whereValues, err := where.ConvertToGormWhere(wheres)
	if err != nil {
		return err
	}
	entityInfo := new(TEntity)
	tx = tx.Where(whereStr, whereValues...)
	if !softDelete {
		//Unscoped可以在执行时忽略软删除功能，实现真实删除
		tx = tx.Unscoped()
	}
	err = tx.Delete(entityInfo).Error
	if err != nil {
		tx.Rollback() //回滚
		return err
	}
	tx.Commit() //提交事务
	return nil
}

// GetTotal 获取符合某个条件的数量
func GetTotal[TEntity Entities](wheres []*where.Condition) (int64, error) {
	var count int64
	whereStr, whereValues, err := where.ConvertToGormWhere(wheres)
	if err != nil {
		return -1, err
	}
	entityInfo := new(TEntity)
	err = dbinit.GetDbInstance().
		Model(entityInfo).Where(whereStr, whereValues...).
		Count(&count).Error
	if err != nil {
		return -1, err
	}
	return count, nil
}
